﻿using System;
using System.IO;

namespace Musi.Core.CSV
{
    public class CSVParser
    {
        public static readonly (int, int) NotFindTarget = (-1, -1);

        private readonly CSVData[] csvDatas;

        public int Row => csvDatas.Length;

        public CSVData this[int index] => csvDatas[index];

        #region Constructor
        /// <summary>
        /// Read CSV from the string of csv
        /// </summary>
        /// <param name="sourceCSVContent"></param>
        public CSVParser(string sourceCSVContent)
        {
            csvDatas = CSVReader.CSVRead(sourceCSVContent);
        }

        /// <summary>
        /// Read CSV from the csv file. After read, should close the `StringReader` by your self.
        /// </summary>
        /// <param name="targetFile"></param>
        public CSVParser(StringReader targetFile)
        {
            csvDatas = CSVReader.CSVRead(targetFile.ReadToEnd());
        }
        #endregion

        #region Member Method
        public string GetDataString(int row, int col)
        {
            return csvDatas[row][col];
        }

        public string GetDataString((int, int) pos)
        {
            return csvDatas[pos.Item1][pos.Item2];
        }

        /// <summary>
        /// If find the target, return (row, col). Return (-1, -1) if not find.
        /// </summary>
        public (int, int) Find(string target)
        {
            (int, int) pos;
            for(int row = 0;row < csvDatas.Length;row ++)
            {
                pos = (row, 0);
                foreach (string data in csvDatas[row])
                {
                    if (data == target)
                    {
                        return pos;
                    }
                    pos.Item2++;
                }
            }

            return (-1, -1);
        }

        public Result<int> ParseInt(int row, int col)
        {
            if (csvDatas[row][col] == null || csvDatas[row][col].Trim() == "")
                return Result<int>.Err($"Parse occur error! Line: {row}, Column: {col}");

            return Result<int>.Ok(Convert.ToInt32(csvDatas[row][col]));
        }

        public Result<int> ParseInt((int, int) pos)
        {
            return ParseInt(pos.Item1, pos.Item2);
        }

        public Result<T> Parse<T>(int row, int col)
            where T: ICSVParse<T>, new()
        {
            T res = new T().CSVParse(GetDataString(row, col));
            if (res == null)
                return Result<T>.Err($"Parse occur error! Line: {row}, Column: {col}");
            else
                return Result<T>.Ok(res);
        }

        public Result<T> Parse<T>((int, int) pos)
            where T : ICSVParse<T>, new()
        {
            return Parse<T>(pos.Item1, pos.Item2);
        }

        public Result<T> ParseList<T>(int row)
            where T: ICSVParseList<T>, new()
        {
            T res = new T().CSVParseList(csvDatas[row].ToArray());

            if (res == null)
                return Result<T>.Err($"Parse occur error! Line: {row}");
            else
                return Result<T>.Ok(res);
        }

        public Result<(int, int)> ParseTupleInt(int row, int col)
        {
            if (csvDatas[row][col] == null || csvDatas[row][col].Trim() == "")
                return Result<(int, int)>.Err($"Don't have content! Line:{row}, {col}");

            return CSVParser.ParseTupleInt(csvDatas[row][col]);
        }
        #endregion

        #region Static Method
        public static Result<T> Parse<T>(string source)
            where T : ICSVParse<T>, new()
        {
            T res = new T().CSVParse(source);
            if (res == null)
                return Result<T>.Err($"Parse occur error! Source: {source}");
            else
                return Result<T>.Ok(res);
        }

        public static Result<(int, int)> ParseTupleInt(string source)
        {
            if (source == null || source.Trim() == "")
                return Result<(int, int)>.Err($"Not content to parse tuple int! Source: {source}");

            string[] groupNumber = source.Replace('(', ' ').Replace(')', ' ').Trim().Split(',');
            if (groupNumber[0] == "" || groupNumber[1] == "")
                return Result<(int, int)>.Err($"Not content to parse tuple int! Source: {source}");

            return Result<(int, int)>.Ok(
                (Convert.ToInt32(groupNumber[0]), Convert.ToInt32(groupNumber[1]))
            );
        }
        #endregion
    }
}
